{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Introduction to atomman: system_model conversions\n",
    "\n",
    "__Lucas M. Hale__, [lucas.hale@nist.gov](mailto:lucas.hale@nist.gov?Subject=ipr-demo), _Materials Science and Engineering Division, NIST_.\n",
    "    \n",
    "[Disclaimers](http://www.nist.gov/public_affairs/disclaimer.cfm) "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 1. Introduction<a id='section1'></a>\n",
    "\n",
    "The system_model format provides a direct representation of an atomman.System object that can be equivalently saved as either JSON or XML atomman.System class. As it is specifically designed for the System class, it captures all information about the system.  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1.1. Notes on the system model format\n",
    "\n",
    "The system model format was updated starting atomman version 1.2.7.  This format change was done to provide consistent representation of the system in data model format with respect to other atomman objects and to ensure that all data defining the System class is captured in the data model.  Subsequent versions of atomman may add fields to the model if the System class adds representations for them, but will likely remain compatible with any version of atomman after 1.2.7.  Unfortunately, the system models generated by versions of atomman <= 1.2.6 are distinctly different and therefore not compatible or supported anymore.\n",
    "\n",
    "The sytem model format is a tree-like structure, with a single root element, \"atomic-system\", and multiple subelements. For consistency with the other atomman objects, the System model contains the models for the system's box and atoms. Expressing paths using periods to separate elements and subelements, the model consists of: \n",
    "\n",
    "- \"atomic-system.box\" is the model for the System's Box containing values for the vects and the origin.\n",
    "- \"atomic-system.periodic-boundary-condition\" lists the three boolean pbc values.\n",
    "- \"atomic-system.atomic-type-symbol\" lists the symbols associated with each atom type.\n",
    "- \"atomic-system.atomic-type-mass\" *Added version 1.3.0* lists the masses associated with each atom type, if any were assigned.\n",
    "- \"atomic-system.atoms\" is the model for the System's Atoms and contains all per-atom values.\n",
    "\n",
    "Consistent with atomman.unitconvert.model(), all multidimensional data is represented as a flattened array combined with shape parameters.  This choice allows the data model to be equivalently represented as JSON or XML while remaining optimized for JSON/Python-based handling."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Library Imports**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "atomman version = 1.4.0\n",
      "Notebook executed on 2021-08-03\n"
     ]
    }
   ],
   "source": [
    "# Standard Python libraries\n",
    "import os\n",
    "import datetime\n",
    "\n",
    "# http://www.numpy.org/\n",
    "import numpy as np\n",
    "\n",
    "# https://github.com/usnistgov/DataModelDict\n",
    "from DataModelDict import DataModelDict as DM\n",
    "\n",
    "# https://github.com/usnistgov/atomman\n",
    "import atomman as am\n",
    "import atomman.unitconvert as uc\n",
    "\n",
    "# Show atomman version\n",
    "print('atomman version =', am.__version__)\n",
    "\n",
    "# Show date of Notebook execution\n",
    "print('Notebook executed on', datetime.date.today())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Generate test system information (CsCl)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "avect =  [ 3.200,  0.000,  0.000]\n",
      "bvect =  [ 0.000,  3.200,  0.000]\n",
      "cvect =  [ 0.000,  0.000,  3.200]\n",
      "origin = [ 0.000,  0.000,  0.000]\n",
      "natoms = 2\n",
      "natypes = 2\n",
      "symbols = ('Cs', 'Cl')\n",
      "pbc = [ True  True  True]\n",
      "per-atom properties = ['atype', 'pos', 'charge', 'stress']\n",
      "     id |   atype |  pos[0] |  pos[1] |  pos[2]\n",
      "      0 |       1 |   0.000 |   0.000 |   0.000\n",
      "      1 |       2 |   1.600 |   1.600 |   1.600\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>atype</th>\n",
       "      <th>pos[0]</th>\n",
       "      <th>pos[1]</th>\n",
       "      <th>pos[2]</th>\n",
       "      <th>charge</th>\n",
       "      <th>stress[0][0]</th>\n",
       "      <th>stress[0][1]</th>\n",
       "      <th>stress[0][2]</th>\n",
       "      <th>stress[1][0]</th>\n",
       "      <th>stress[1][1]</th>\n",
       "      <th>stress[1][2]</th>\n",
       "      <th>stress[2][0]</th>\n",
       "      <th>stress[2][1]</th>\n",
       "      <th>stress[2][2]</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>1</td>\n",
       "      <td>2</td>\n",
       "      <td>1.6</td>\n",
       "      <td>1.6</td>\n",
       "      <td>1.6</td>\n",
       "      <td>-1.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "   atype  pos[0]  pos[1]  pos[2]  charge  stress[0][0]  stress[0][1]  \\\n",
       "0      1     0.0     0.0     0.0     1.0           0.0           0.0   \n",
       "1      2     1.6     1.6     1.6    -1.0           0.0           0.0   \n",
       "\n",
       "   stress[0][2]  stress[1][0]  stress[1][1]  stress[1][2]  stress[2][0]  \\\n",
       "0           0.0           0.0           0.0           0.0           0.0   \n",
       "1           0.0           0.0           0.0           0.0           0.0   \n",
       "\n",
       "   stress[2][1]  stress[2][2]  \n",
       "0           0.0           0.0  \n",
       "1           0.0           0.0  "
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Generate box\n",
    "alat = uc.set_in_units(3.2, 'angstrom')\n",
    "box = am.Box(a=alat, b=alat, c=alat)\n",
    "\n",
    "# Generate atoms with atype, pos, charge, and stress properties\n",
    "atype = [1, 2]\n",
    "pos = [[0,0,0], [0.5, 0.5, 0.5]]\n",
    "charge = uc.set_in_units([1, -1], 'e')\n",
    "stress = uc.set_in_units(np.zeros((2, 3, 3)), 'MPa')\n",
    "atoms = am.Atoms(pos=pos, atype=atype, charge=charge, stress=stress)\n",
    "\n",
    "# Build system from box and atoms, and scale atoms\n",
    "system = am.System(atoms=atoms, box=box, scale=True, symbols=['Cs', 'Cl'])\n",
    "\n",
    "# Print system information\n",
    "print(system)\n",
    "system.atoms_df()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 2. Dump<a id='section2'></a>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2.1. System.model()\n",
    "\n",
    "Similar to other atomman classes, the System class has a model() method that generates a data model representation for the object. This allows for all content of the system to be saved as either JSON or XML, and reloaded later by initializing a new System object using the model.\n",
    "\n",
    "Parameters\n",
    "        \n",
    "- __box_unit__ (*str, optional*) Length unit to use for the box. Default value is 'angstrom'.\n",
    "\n",
    "- __prop_name__ (*list, optional*) The Atoms properties to include.  If neither prop_name nor prop_unit are given, all system properties will be included.\n",
    "\n",
    "- __unit__ (*list, optional*) Lists the units for each prop_name as stored in the table.  For a value of None, no conversion will be performed for that property.  For a value of 'scaled', the corresponding table values will be taken in box-scaled units.  If neither unit nor prop_units given, pos will be given in Angstroms and all other values will not be converted.\n",
    "\n",
    "- __prop_unit__ (*dict, optional*) dictionary where the keys are the property keys to include, and the values are units to use. If neither unit nor prop_units given, pos will be given in Angstroms and all other values will not be converted.\n",
    "\n",
    "Returns\n",
    "\n",
    "- (*DataModelDict.DataModelDict*) A JSON/XML data model for the current System object. "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 2.1.1. Simple example"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "DataModelDict([('atomic-system', DataModelDict([('box', DataModelDict([('avect', DataModelDict([('value', [3.2, 0.0, 0.0])])), ('bvect', DataModelDict([('value', [0.0, 3.2, 0.0])])), ('cvect', DataModelDict([('value', [0.0, 0.0, 3.2])])), ('origin', DataModelDict([('value', [0.0, 0.0, 0.0])]))])), ('periodic-boundary-condition', [True, True, True]), ('atom-type-symbol', ['Cs', 'Cl']), ('atoms', DataModelDict([('natoms', 2), ('property', [DataModelDict([('name', 'atype'), ('data', DataModelDict([('value', [1, 2])]))]), DataModelDict([('name', 'pos'), ('data', DataModelDict([('value', [0.0, 0.0, 0.0, 1.6, 1.6, 1.6]), ('shape', [2, 3]), ('unit', 'angstrom')]))]), DataModelDict([('name', 'charge'), ('data', DataModelDict([('value', [1.0, -1.0])]))]), DataModelDict([('name', 'stress'), ('data', DataModelDict([('value', [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]), ('shape', [2, 3, 3])]))])])]))]))])\n"
     ]
    }
   ],
   "source": [
    "# Retrieve model as a DataModelDict using model\n",
    "model = system.model()\n",
    "print(model)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{\"atomic-system\": {\"box\": {\"avect\": {\"value\": [3.2, 0.0, 0.0]}, \"bvect\": {\"value\": [0.0, 3.2, 0.0]}, \"cvect\": {\"value\": [0.0, 0.0, 3.2]}, \"origin\": {\"value\": [0.0, 0.0, 0.0]}}, \"periodic-boundary-condition\": [true, true, true], \"atom-type-symbol\": [\"Cs\", \"Cl\"], \"atoms\": {\"natoms\": 2, \"property\": [{\"name\": \"atype\", \"data\": {\"value\": [1, 2]}}, {\"name\": \"pos\", \"data\": {\"value\": [0.0, 0.0, 0.0, 1.6, 1.6, 1.6], \"shape\": [2, 3], \"unit\": \"angstrom\"}}, {\"name\": \"charge\", \"data\": {\"value\": [1.0, -1.0]}}, {\"name\": \"stress\", \"data\": {\"value\": [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], \"shape\": [2, 3, 3]}}]}}}\n"
     ]
    }
   ],
   "source": [
    "# Convert model to JSON\n",
    "print(model.json())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n",
      "<atomic-system><box><avect><value>3.2</value><value>0.0</value><value>0.0</value></avect><bvect><value>0.0</value><value>3.2</value><value>0.0</value></bvect><cvect><value>0.0</value><value>0.0</value><value>3.2</value></cvect><origin><value>0.0</value><value>0.0</value><value>0.0</value></origin></box><periodic-boundary-condition>True</periodic-boundary-condition><periodic-boundary-condition>True</periodic-boundary-condition><periodic-boundary-condition>True</periodic-boundary-condition><atom-type-symbol>Cs</atom-type-symbol><atom-type-symbol>Cl</atom-type-symbol><atoms><natoms>2</natoms><property><name>atype</name><data><value>1</value><value>2</value></data></property><property><name>pos</name><data><value>0.0</value><value>0.0</value><value>0.0</value><value>1.6</value><value>1.6</value><value>1.6</value><shape>2</shape><shape>3</shape><unit>angstrom</unit></data></property><property><name>charge</name><data><value>1.0</value><value>-1.0</value></data></property><property><name>stress</name><data><value>0.0</value><value>0.0</value><value>0.0</value><value>0.0</value><value>0.0</value><value>0.0</value><value>0.0</value><value>0.0</value><value>0.0</value><value>0.0</value><value>0.0</value><value>0.0</value><value>0.0</value><value>0.0</value><value>0.0</value><value>0.0</value><value>0.0</value><value>0.0</value><shape>2</shape><shape>3</shape><shape>3</shape></data></property></atoms></atomic-system>\n"
     ]
    }
   ],
   "source": [
    "# Convert model to XML\n",
    "print(model.xml())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 2.1.2. Specify units and/or limit included properties\n",
    "\n",
    "By default, all per-atom properties will be saved to the data model.  Since freely-assigned properties can theoretically be in any unit, the values will be saved in atomman's working units if no unit information is provided.  This implicitly assumes that atomman will be used to read the data back in, and that atomman's working units during dumping and loading are the same."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "atype is in units None\n",
      "pos is in units angstrom\n",
      "charge is in units None\n",
      "stress is in units None\n"
     ]
    }
   ],
   "source": [
    "# Show (lack of) units as set in the model above\n",
    "for prop in model.finds('property'):\n",
    "    name = prop['name']\n",
    "    unit = prop['data'].get('unit', None)\n",
    "    print(f'{name} is in units {unit}')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The units that the values are saved in can be explicitly set by providing a list that gives a unit for each of the set per-atom properties in the order that System.atoms_prop() lists them."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "atype is in units None\n",
      "pos is in units nm\n",
      "charge is in units e\n",
      "stress is in units GPa\n"
     ]
    }
   ],
   "source": [
    "model2 = system.model(unit=[None, 'nm', 'e', 'GPa'])\n",
    "\n",
    "# Show units are now assigned\n",
    "for prop in model2.finds('property'):\n",
    "    name = prop['name']\n",
    "    unit = prop['data'].get('unit', None)\n",
    "    print(f'{name} is in units {unit}')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Properties can also be excluded from the data model by using prop_name to list only the wanted properties.  In that case, the unit values should match with the prop_name values."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "atype is in units None\n",
      "pos is in units nm\n",
      "stress is in units GPa\n"
     ]
    }
   ],
   "source": [
    "model2 = system.model(prop_name=['atype', 'pos', 'stress'],\n",
    "                      unit=[None, 'nm', 'GPa'])\n",
    "\n",
    "# Show units are now assigned\n",
    "for prop in model2.finds('property'):\n",
    "    name = prop['name']\n",
    "    unit = prop['data'].get('unit', None)\n",
    "    print(f'{name} is in units {unit}')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "For convenience, the unit and property choice can alternatively be represented in dictionary format and passed in using the prop_unit parameter."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "atype is in units None\n",
      "pos is in units nm\n",
      "charge is in units e\n"
     ]
    }
   ],
   "source": [
    "prop_unit = {}\n",
    "prop_unit['atype'] = None\n",
    "prop_unit['pos'] = 'nm'\n",
    "prop_unit['charge'] = 'e'\n",
    "\n",
    "model2 = system.model(prop_unit=prop_unit)\n",
    "\n",
    "# Show units are now assigned\n",
    "for prop in model2.finds('property'):\n",
    "    name = prop['name']\n",
    "    unit = prop['data'].get('unit', None)\n",
    "    print(f'{name} is in units {unit}')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2.2. System.dump('system_model')\n",
    "\n",
    "Alternatively, a model of the system can be generated by calling the System.dump() method using the 'system_model' style. This allows for consistency with the other System-level conversions. There is no difference in the resulting models produced by the two methods as System.dump() calls System.model().   \n",
    "\n",
    "Parameters\n",
    "\n",
    "- **f** (*str or file-like object, optional*) File path or file-like object to write the content to.  If not given, then the content is returned.\n",
    "\n",
    "- **box_unit** (*str, optional*) Length unit to use for the box. Default value is 'angstrom'.\n",
    "\n",
    "- **prop_name** (*list, optional*) The Atoms properties to include.  If neither prop_name nor prop_unit are given, all system properties will be included.\n",
    "\n",
    "- **unit** (*list, optional*) Lists the units for each prop_name as stored in the table.  For a value of None, no conversion will be performed for that property.  For a value of 'scaled', the corresponding table values will be taken in box-scaled units.  If neither unit nor prop_units given, pos will be given in Angstroms and all other values will not be converted.\n",
    "\n",
    "- **prop_unit** (*dict, optional*) dictionary where the keys are the property keys to include, and the values are units to use. If neither unit nor prop_units given, pos will be given in Angstroms and all other values will not be converted.\n",
    "        \n",
    "- **format** (*str, optional*) File format 'xml' or 'json' to save the content as if f is given.  If f is a filename, then the format will be automatically inferred from f's extension.  If format is not given and cannot be inferred, then it will be set to 'json'.\n",
    "\n",
    "- **indent** (*int or None, optional*) Indentation option to use for XML/JSON content if f is given.  A value of None (default) will add no line separatations or indentations.\n",
    "\n",
    "Returns\n",
    "\n",
    "- **model** (*DataModelDict.DataModelDict or str*) The generated model representation of the system.  Will be a DataModelDict if format is not specified, and a JSON- or XML-formatted string if format is specified.  Returned if f is not given."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 2.2.1. Simple example\n",
    "\n",
    "As System.dump('system_model') calls System.model(), most parameters of the two functions are the same."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "DataModelDict([('atomic-system', DataModelDict([('box', DataModelDict([('avect', DataModelDict([('value', [3.2, 0.0, 0.0])])), ('bvect', DataModelDict([('value', [0.0, 3.2, 0.0])])), ('cvect', DataModelDict([('value', [0.0, 0.0, 3.2])])), ('origin', DataModelDict([('value', [0.0, 0.0, 0.0])]))])), ('periodic-boundary-condition', [True, True, True]), ('atom-type-symbol', ['Cs', 'Cl']), ('atoms', DataModelDict([('natoms', 2), ('property', [DataModelDict([('name', 'atype'), ('data', DataModelDict([('value', [1, 2])]))]), DataModelDict([('name', 'pos'), ('data', DataModelDict([('value', [0.0, 0.0, 0.0, 0.5, 0.5, 0.5]), ('shape', [2, 3]), ('unit', 'scaled')]))]), DataModelDict([('name', 'charge'), ('data', DataModelDict([('value', [1.0, -1.0]), ('unit', 'e')]))]), DataModelDict([('name', 'stress'), ('data', DataModelDict([('value', [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]), ('shape', [2, 3, 3]), ('unit', 'GPa')]))])])]))]))])\n"
     ]
    }
   ],
   "source": [
    "model = system.dump('system_model', prop_unit={'atype':None, 'pos':'scaled', 'charge': 'e', 'stress': 'GPa'})\n",
    "print(model)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The primary difference is that System.dump('atom_model') can directly convert to JSON/XML and save to a file."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{\n",
      "  \"atomic-system\": {\n",
      "    \"box\": {\n",
      "      \"avect\": {\n",
      "        \"value\": [\n",
      "          3.2,\n",
      "          0.0,\n",
      "          0.0\n",
      "        ]\n",
      "      },\n",
      "      \"bvect\": {\n",
      "        \"value\": [\n",
      "          0.0,\n",
      "          3.2,\n",
      "          0.0\n",
      "        ]\n",
      "      },\n",
      "      \"cvect\": {\n",
      "        \"value\": [\n",
      "          0.0,\n",
      "          0.0,\n",
      "          3.2\n",
      "        ]\n",
      "      },\n",
      "      \"origin\": {\n",
      "        \"value\": [\n",
      "          0.0,\n",
      "          0.0,\n",
      "          0.0\n",
      "        ]\n",
      "      }\n",
      "    },\n",
      "    \"periodic-boundary-condition\": [\n",
      "      true,\n",
      "      true,\n",
      "      true\n",
      "    ],\n",
      "    \"atom-type-symbol\": [\n",
      "      \"Cs\",\n",
      "      \"Cl\"\n",
      "    ],\n",
      "    \"atoms\": {\n",
      "      \"natoms\": 2,\n",
      "      \"property\": [\n",
      "        {\n",
      "          \"name\": \"atype\",\n",
      "          \"data\": {\n",
      "            \"value\": [\n",
      "              1,\n",
      "              2\n",
      "            ]\n",
      "          }\n",
      "        },\n",
      "        {\n",
      "          \"name\": \"pos\",\n",
      "          \"data\": {\n",
      "            \"value\": [\n",
      "              0.0,\n",
      "              0.0,\n",
      "              0.0,\n",
      "              0.5,\n",
      "              0.5,\n",
      "              0.5\n",
      "            ],\n",
      "            \"shape\": [\n",
      "              2,\n",
      "              3\n",
      "            ],\n",
      "            \"unit\": \"scaled\"\n",
      "          }\n",
      "        }\n",
      "      ]\n",
      "    }\n",
      "  }\n",
      "}\n"
     ]
    }
   ],
   "source": [
    "model_json = system.dump('system_model', format='json', indent=2, \n",
    "                         prop_unit={'atype':None, 'pos':'scaled'})\n",
    "print(model_json)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n",
      "<atomic-system><box><avect><value>3.2</value><value>0.0</value><value>0.0</value></avect><bvect><value>0.0</value><value>3.2</value><value>0.0</value></bvect><cvect><value>0.0</value><value>0.0</value><value>3.2</value></cvect><origin><value>0.0</value><value>0.0</value><value>0.0</value></origin></box><periodic-boundary-condition>True</periodic-boundary-condition><periodic-boundary-condition>True</periodic-boundary-condition><periodic-boundary-condition>True</periodic-boundary-condition><atom-type-symbol>Cs</atom-type-symbol><atom-type-symbol>Cl</atom-type-symbol><atoms><natoms>2</natoms><property><name>atype</name><data><value>1</value><value>2</value></data></property><property><name>pos</name><data><value>0.0</value><value>0.0</value><value>0.0</value><value>0.5</value><value>0.5</value><value>0.5</value><shape>2</shape><shape>3</shape><unit>scaled</unit></data></property></atoms></atomic-system>\n"
     ]
    }
   ],
   "source": [
    "# Save to file as XML\n",
    "system.dump('system_model', f='model.xml', format='xml',\n",
    "            prop_unit={'atype':None, 'pos':'scaled'})\n",
    "\n",
    "with open('model.xml') as f:\n",
    "    print(f.read())\n",
    "    \n",
    "os.remove('model.xml')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 3. Load<a id='section3'></a>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3.1. System.__init__(model)\n",
    "\n",
    "A model can be interpreted by passing it as a parameter when initializing a new System object.  Note that the supplied model value can be a DataModelDict, a JSON or XML string, or the name of a JSON or XML file."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "avect =  [ 3.200,  0.000,  0.000]\n",
      "bvect =  [ 0.000,  3.200,  0.000]\n",
      "cvect =  [ 0.000,  0.000,  3.200]\n",
      "origin = [ 0.000,  0.000,  0.000]\n",
      "natoms = 2\n",
      "natypes = 2\n",
      "symbols = ('Cs', 'Cl')\n",
      "pbc = [ True  True  True]\n",
      "per-atom properties = ['atype', 'pos', 'charge', 'stress']\n",
      "     id |   atype |  pos[0] |  pos[1] |  pos[2]\n",
      "      0 |       1 |   0.000 |   0.000 |   0.000\n",
      "      1 |       2 |   1.600 |   1.600 |   1.600\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>atype</th>\n",
       "      <th>pos[0]</th>\n",
       "      <th>pos[1]</th>\n",
       "      <th>pos[2]</th>\n",
       "      <th>charge</th>\n",
       "      <th>stress[0][0]</th>\n",
       "      <th>stress[0][1]</th>\n",
       "      <th>stress[0][2]</th>\n",
       "      <th>stress[1][0]</th>\n",
       "      <th>stress[1][1]</th>\n",
       "      <th>stress[1][2]</th>\n",
       "      <th>stress[2][0]</th>\n",
       "      <th>stress[2][1]</th>\n",
       "      <th>stress[2][2]</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>1</td>\n",
       "      <td>2</td>\n",
       "      <td>1.6</td>\n",
       "      <td>1.6</td>\n",
       "      <td>1.6</td>\n",
       "      <td>-1.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "   atype  pos[0]  pos[1]  pos[2]  charge  stress[0][0]  stress[0][1]  \\\n",
       "0      1     0.0     0.0     0.0     1.0           0.0           0.0   \n",
       "1      2     1.6     1.6     1.6    -1.0           0.0           0.0   \n",
       "\n",
       "   stress[0][2]  stress[1][0]  stress[1][1]  stress[1][2]  stress[2][0]  \\\n",
       "0           0.0           0.0           0.0           0.0           0.0   \n",
       "1           0.0           0.0           0.0           0.0           0.0   \n",
       "\n",
       "   stress[2][1]  stress[2][2]  \n",
       "0           0.0           0.0  \n",
       "1           0.0           0.0  "
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Initialize new system using model\n",
    "system2 = am.System(model=model)\n",
    "\n",
    "# Print system information\n",
    "print(system2)\n",
    "system2.atoms_df()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3.2. atomman.load('system_model')\n",
    "\n",
    "The atomman.load() function with the'system_model' style also supports reading System.model content.  \n",
    "\n",
    "Parameters\n",
    "\n",
    "- **model** (*str, file-like object or DataModelDict*) The data model to read.\n",
    "\n",
    "- **symbols** (*tuple, optional*) Allows the list of element symbols to be assigned during loading.\n",
    "\n",
    "- **key** (*str, optional*) The key identifying the root element for the system definition. Default value is 'atomic-system'.\n",
    "\n",
    "- **index** (*int, optional*) If the full model has multiple key entries, the index specifies which to access.  Default value is 0 (first, or only entry).\n",
    "\n",
    "Returns\n",
    "\n",
    "- **system** (*atomman.System*) The system object associated with the data model."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 3.2.1. Examples\n",
    "\n",
    "The default behavior of atomman.load() is identical to initializing a new System object using the model."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "avect =  [ 3.200,  0.000,  0.000]\n",
      "bvect =  [ 0.000,  3.200,  0.000]\n",
      "cvect =  [ 0.000,  0.000,  3.200]\n",
      "origin = [ 0.000,  0.000,  0.000]\n",
      "natoms = 2\n",
      "natypes = 2\n",
      "symbols = ('Cs', 'Cl')\n",
      "pbc = [ True  True  True]\n",
      "per-atom properties = ['atype', 'pos', 'charge', 'stress']\n",
      "     id |   atype |  pos[0] |  pos[1] |  pos[2]\n",
      "      0 |       1 |   0.000 |   0.000 |   0.000\n",
      "      1 |       2 |   1.600 |   1.600 |   1.600\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>atype</th>\n",
       "      <th>pos[0]</th>\n",
       "      <th>pos[1]</th>\n",
       "      <th>pos[2]</th>\n",
       "      <th>charge</th>\n",
       "      <th>stress[0][0]</th>\n",
       "      <th>stress[0][1]</th>\n",
       "      <th>stress[0][2]</th>\n",
       "      <th>stress[1][0]</th>\n",
       "      <th>stress[1][1]</th>\n",
       "      <th>stress[1][2]</th>\n",
       "      <th>stress[2][0]</th>\n",
       "      <th>stress[2][1]</th>\n",
       "      <th>stress[2][2]</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>1</td>\n",
       "      <td>2</td>\n",
       "      <td>1.6</td>\n",
       "      <td>1.6</td>\n",
       "      <td>1.6</td>\n",
       "      <td>-1.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "   atype  pos[0]  pos[1]  pos[2]  charge  stress[0][0]  stress[0][1]  \\\n",
       "0      1     0.0     0.0     0.0     1.0           0.0           0.0   \n",
       "1      2     1.6     1.6     1.6    -1.0           0.0           0.0   \n",
       "\n",
       "   stress[0][2]  stress[1][0]  stress[1][1]  stress[1][2]  stress[2][0]  \\\n",
       "0           0.0           0.0           0.0           0.0           0.0   \n",
       "1           0.0           0.0           0.0           0.0           0.0   \n",
       "\n",
       "   stress[2][1]  stress[2][2]  \n",
       "0           0.0           0.0  \n",
       "1           0.0           0.0  "
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "model_system = am.load('system_model', model)\n",
    "print(model_system)\n",
    "model_system.atoms_df()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The advantage of using atomman.load('system_model') is that it is designed to also handle larger data models that may contain embedded system model elements.  Depending on what the larger data model represents, multiple system data models may be embedded as a list or differentiated by different root element keys. The key and index parameters of atomman.load('system_model') therefore make it possible to uniquely select one system model from within a larger data model."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "avect =  [ 3.200,  0.000,  0.000]\n",
      "bvect =  [ 0.000,  3.200,  0.000]\n",
      "cvect =  [ 0.000,  0.000,  3.200]\n",
      "origin = [ 0.000,  0.000,  0.000]\n",
      "natoms = 2\n",
      "natypes = 2\n",
      "symbols = ('Cs', 'Cl')\n",
      "pbc = [ True  True  True]\n",
      "per-atom properties = ['atype', 'pos', 'charge', 'stress']\n",
      "     id |   atype |  pos[0] |  pos[1] |  pos[2]\n",
      "      0 |       1 |   0.000 |   0.000 |   0.000\n",
      "      1 |       2 |   1.600 |   1.600 |   1.600\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>atype</th>\n",
       "      <th>pos[0]</th>\n",
       "      <th>pos[1]</th>\n",
       "      <th>pos[2]</th>\n",
       "      <th>charge</th>\n",
       "      <th>stress[0][0]</th>\n",
       "      <th>stress[0][1]</th>\n",
       "      <th>stress[0][2]</th>\n",
       "      <th>stress[1][0]</th>\n",
       "      <th>stress[1][1]</th>\n",
       "      <th>stress[1][2]</th>\n",
       "      <th>stress[2][0]</th>\n",
       "      <th>stress[2][1]</th>\n",
       "      <th>stress[2][2]</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>1</td>\n",
       "      <td>2</td>\n",
       "      <td>1.6</td>\n",
       "      <td>1.6</td>\n",
       "      <td>1.6</td>\n",
       "      <td>-1.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "   atype  pos[0]  pos[1]  pos[2]  charge  stress[0][0]  stress[0][1]  \\\n",
       "0      1     0.0     0.0     0.0     1.0           0.0           0.0   \n",
       "1      2     1.6     1.6     1.6    -1.0           0.0           0.0   \n",
       "\n",
       "   stress[0][2]  stress[1][0]  stress[1][1]  stress[1][2]  stress[2][0]  \\\n",
       "0           0.0           0.0           0.0           0.0           0.0   \n",
       "1           0.0           0.0           0.0           0.0           0.0   \n",
       "\n",
       "   stress[2][1]  stress[2][2]  \n",
       "0           0.0           0.0  \n",
       "1           0.0           0.0  "
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Define a larger data model\n",
    "collection_model = DM()\n",
    "collection_model['system-collection'] = DM()\n",
    "\n",
    "# Add multiple system models under the test-atomic-system key\n",
    "collection_model['system-collection'].append('test-atomic-system', model['atomic-system'])\n",
    "collection_model['system-collection'].append('test-atomic-system', model['atomic-system'])\n",
    "\n",
    "# Use atomman.load() to load the second 'test-atomic-system' model\n",
    "system3 = am.load('system_model', collection_model, index=1, key='test-atomic-system')\n",
    "print(system3)\n",
    "system3.atoms_df()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    " "
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
